/**
 * TypeScript Type Enforcement Examples
 *
 * This file demonstrates that TypeScript properly enforces types
 * when using schemas with createSuite. Uncomment the error examples
 * to see TypeScript compilation errors.
 */

import { enforce } from 'n4s';
import { describe, expect, it } from 'vitest';

import { create, test } from 'vest';

// ✅ CORRECT: Data matches schema
const userSchema = enforce.shape({
  username: enforce.isString(),
  age: enforce.isNumber(),
  email: enforce.isString(),
});

const validSuite = create(data => {
  // TypeScript knows the exact shape of data:
  // data.username: string
  // data.age: number
  // data.email: string

  test('username', () => {
    enforce(data.username).isNotEmpty();
  });

  test('age', () => {
    enforce(data.age).greaterThan(0);
  });
}, userSchema);

// ✅ LOOSE SCHEMA: Allows extra properties
const looseSchema = enforce.loose({
  id: enforce.isNumber(),
  name: enforce.isString(),
});

const looseSuite = create(data => {
  // TypeScript knows about id and name
  test('id', () => {
    enforce(data.id).isNumber();
  });
}, looseSchema);

// ✅ NESTED SCHEMAS: Full type inference
const addressSchema = enforce.shape({
  street: enforce.isString(),
  city: enforce.isString(),
  zipCode: enforce.isString(),
});

const personSchema = enforce.shape({
  name: enforce.isString(),
  address: addressSchema,
});

const nestedSuite = create(data => {
  // TypeScript knows the full nested structure
  // data.name: string
  // data.address.street: string
  // data.address.city: string
  // data.address.zipCode: string

  test('city', () => {
    enforce(data.address.city).isNotEmpty();
  });
}, personSchema);

// ❌ Should fail: callback parameter not typed from schema
const schema = enforce.shape({
  name: enforce.isString(),
  age: enforce.isNumber(),
});

create(data => {
  // @ts-expect-error - Invalid data
  void data.nonexistent;
  test('name', () => {
    enforce(data.name).isNotEmpty();
  });
}, schema);

// ✅ Should allow generic typing when no schema
const suite2 = create((data: { foo: string; bar: number }) => {
  test('foo', () => {
    enforce(data.foo).isNotEmpty();
  });
  test('bar', () => {
    enforce(data.bar).greaterThan(0);
  });
});

describe('schema typing examples', () => {
  it('runs strict schema suites with correct data', () => {
    expect(() =>
      validSuite.run({
        username: 'john',
        age: 30,
        email: 'john@example.com',
      }),
    ).not.toThrow();
  });

  it('allows extra properties with loose schema', () => {
    expect(() =>
      looseSuite.run({
        id: 1,
        name: 'Test',
        extra: 'This is fine with loose schema',
        another: 42,
      }),
    ).not.toThrow();
  });

  it('resolves nested schemas', () => {
    expect(() =>
      nestedSuite.run({
        name: 'John',
        address: {
          street: '123 Main St',
          city: 'Springfield',
          zipCode: '12345',
        },
      }),
    ).not.toThrow();
  });

  it('accepts correct shapes when no schema but explicit generic is provided', () => {
    expect(() => suite2.run({ foo: 'baz', bar: 1 })).not.toThrow();
  });
});

export { validSuite, looseSuite, nestedSuite };
